#ifndef _SHMEM_H_
#define _SHMEM_H_

#pragma once

#pragma warning(push)
#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4214) // bit field types other than int

#include <ntddk.h>

#pragma warning(pop)

typedef struct IVSHMEM_MMAP_CONFIG
{
    UINT8 cacheMode; // the caching mode of the mapping, see IVSHMEM_CACHE_* for options
}
IVSHMEM_MMAP_CONFIG, * PIVSHMEM_MMAP_CONFIG;


/*
   This structure is for use with the IOCTL_IVSHMEM_REGISTER_EVENT IOCTL

   Please Note:
     - The IVSHMEM driver has a hard limit of 32 events.
     - Events that are singleShot are released after they have been set.
     - At this time repeating events are only released when the driver device
       handle is closed, closing the event handle doesn't release it from the
       drivers list. While this won't cause a problem in the driver, it will
       cause you to run out of event slots.
 */
typedef struct IVSHMEM_EVENT
{
    UINT16  vector;     // the vector that triggers the event
    HANDLE  event;      // the event to trigger
    BOOLEAN singleShot; // set to TRUE if you want the driver to only trigger this event once
}
IVSHMEM_EVENT, * PIVSHMEM_EVENT;

typedef enum REG_SIZE {
    byte = 1,
    word = 2,
    dword = 4,
} REG_SIZE;

typedef struct IVSHMEM_REG
{
    UINT16    offset;     // the offset of target reg from the begining of BAR0
    REG_SIZE  regSize;    // data size of target reg
    union {
        UINT8 data[4];
        UINT8 byte;
        UINT16 word;
        UINT32 dword;
    };
}
IVSHMEM_REG, * PIVSHMEM_REG;

typedef
VOID
IVSHMEM_EVENT_CALLBACK_FUNC(
    PVOID   Context,
    UINT64  EventsMask
);
typedef IVSHMEM_EVENT_CALLBACK_FUNC* PIVSHMEM_EVENT_CALLBACK_FUNC;

typedef struct IVSHMEM_EVENT_CALLBACK
{
    // Note: callback will run at DISPATCH_LEVEL
    PIVSHMEM_EVENT_CALLBACK_FUNC    pFunc;
    PVOID                           pContext;
}
IVSHMEM_EVENT_CALLBACK, * PIVSHMEM_EVENT_CALLBACK;

typedef struct WORKITEM_PARAM {
    PVOID ctx;
    UINT64 EventsMask;
    PIO_WORKITEM WorkItem;
    volatile LONG Used;
}
WORKITEM_PARAM, * PWORKITEM_PARAM;

#define IVSHMEM_CACHE_CACHED        1
#define IVSHMEM_BAR0_VOLUME_OFFSET  240
#define IVSHMEM_BAR0_STATUS_OFFSET  244
// 1 means play and 0 means capture
#define IVSHMEM_STATUS_PLAY_BIT 0x1
// 1 means start and 0 means stop
#define IVSHMEM_STATUS_START_BIT 0x2
// 1 means on and 0 means off
#define IVSHMEM_STATUS_MIC_AVAIL_BIT 0x4

#define INVALID_IVSHMEM_STATUS      ((UINT32)-1)

//-----------------------------------------------------------------------------
//  IVSHMEM IOCTLs
//-----------------------------------------------------------------------------

#define IOCTL_IVSHMEM_REQUEST_SIZE   CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_IVSHMEM_RELEASE_MMAP   CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_IVSHMEM_REQUEST_KMAP   CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_IVSHMEM_READ_REG       CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_IVSHMEM_WRITE_REG      CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_IVSHMEM_REGISTER_EVENT_CB  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)

typedef struct IVSHMEM_MMAP
{
    UINT16         peerID;  // our peer id
    UINT64         size;    // the size of the memory region
    PVOID          ptr;     // pointer to the memory region
    UINT16         vectors; // the number of vectors available
}
IVSHMEM_MMAP, * PIVSHMEM_MMAP;

class CSharedMemory {
    BOOLEAN                     m_bInitialized;
    PDEVICE_OBJECT              m_pIVShMemDeviceObject;
    PFILE_OBJECT                m_pIVShMemFileObject;
    IVSHMEM_MMAP                m_mmap;
    PDEVICE_OBJECT              m_pDeviceObject;

public:
    CSharedMemory(IN PDEVICE_OBJECT DeviceObject);
    ~CSharedMemory();

    BOOLEAN                     Initialize();

    PVOID                       GetSharedMemPtr(); // returns pointer to the shared memory
    UINT64                      GetSharedMemSize();
    PDEVICE_OBJECT              GetSharedDeviceObject();
    UINT32                      GetVolumeFromBackend();
    UINT32                      GetStatusFromBackend();
    VOID                        SetVolumeToBackend(UINT32 volume);
    VOID                        NotifyStart(BOOLEAN play, BOOLEAN start);
    BOOLEAN                     RegisterIvshmemEventHandlerDispatch(PVOID Context, PIVSHMEM_EVENT_CALLBACK_FUNC func);

private:
    BOOLEAN                     RequestMMAP();
    void                        ReleaseMMAP();
    BOOLEAN                     ReadWriteRegister(ULONG ioctl_code, PVOID in, ULONG insize, PVOID out, ULONG outsize);
};
#endif
